/*-------------------------------------------------------------------------
 *
 * collationcmds.c
 *	  相关的排序命令支持代码
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/collationcmds.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
#include "commands/alter.h"
#include "commands/collationcmds.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "common/string.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/pg_locale.h"
#include "utils/rel.h"
#include "utils/syscache.h"


typedef struct
{
	char	   *localename;		/* locale的名称，按照“locale -a” */
	char	   *alias;			/* 相同的缩写别名 */
	int			enc;			/* 编码 */
} CollAliasData;


/*
 * 创建排序规则
 */
ObjectAddress DefineCollation(ParseState *fc_pstate, List *fc_names, List *fc_parameters, bool fc_if_not_exists)
{
	char	   *fc_collName;
	Oid			fc_collNamespace;
	AclResult	fc_aclresult;
	ListCell   *fc_pl;
	DefElem    *fc_fromEl = NULL;
	DefElem    *fc_localeEl = NULL;
	DefElem    *fc_lccollateEl = NULL;
	DefElem    *fc_lcctypeEl = NULL;
	DefElem    *fc_providerEl = NULL;
	DefElem    *fc_deterministicEl = NULL;
	DefElem    *fc_versionEl = NULL;
	char	   *fc_collcollate;
	char	   *fc_collctype;
	char	   *fc_colliculocale;
	bool		fc_collisdeterministic;
	int			fc_collencoding;
	char		fc_collprovider;
	char	   *fc_collversion = NULL;
	Oid			fc_newoid;
	ObjectAddress fc_address;

	fc_collNamespace = QualifiedNameGetCreationNamespace(fc_names, &fc_collName);

	fc_aclresult = pg_namespace_aclcheck(fc_collNamespace, GetUserId(), ACL_CREATE);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, OBJECT_SCHEMA,
					   get_namespace_name(fc_collNamespace));

	foreach(fc_pl, fc_parameters)
	{
		DefElem    *fc_defel = lfirst_node(DefElem, fc_pl);
		DefElem   **fc_defelp;

		if (strcmp(fc_defel->defname, "from") == 0)
			fc_defelp = &fc_fromEl;
		else if (strcmp(fc_defel->defname, "locale") == 0)
			fc_defelp = &fc_localeEl;
		else if (strcmp(fc_defel->defname, "lc_collate") == 0)
			fc_defelp = &fc_lccollateEl;
		else if (strcmp(fc_defel->defname, "lc_ctype") == 0)
			fc_defelp = &fc_lcctypeEl;
		else if (strcmp(fc_defel->defname, "provider") == 0)
			fc_defelp = &fc_providerEl;
		else if (strcmp(fc_defel->defname, "deterministic") == 0)
			fc_defelp = &fc_deterministicEl;
		else if (strcmp(fc_defel->defname, "version") == 0)
			fc_defelp = &fc_versionEl;
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("collation attribute \"%s\" not recognized",
							fc_defel->defname),
					 parser_errposition(fc_pstate, fc_defel->location)));
			break;
		}
		if (*fc_defelp != NULL)
			errorConflictingDefElem(fc_defel, fc_pstate);
		*fc_defelp = fc_defel;
	}

	if (fc_localeEl && (fc_lccollateEl || fc_lcctypeEl))
		ereport(ERROR,
				errcode(ERRCODE_SYNTAX_ERROR),
				errmsg("conflicting or redundant options"),
				errdetail("LOCALE cannot be specified together with LC_COLLATE or LC_CTYPE."));

	if (fc_fromEl && list_length(fc_parameters) != 1)
		ereport(ERROR,
				errcode(ERRCODE_SYNTAX_ERROR),
				errmsg("conflicting or redundant options"),
				errdetail("FROM cannot be specified together with any other options."));

	if (fc_fromEl)
	{
		Oid			fc_collid;
		HeapTuple	fc_tp;
		Datum		fc_datum;
		bool		fc_isnull;

		fc_collid = get_collation_oid(defGetQualifiedName(fc_fromEl), false);
		fc_tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(fc_collid));
		if (!HeapTupleIsValid(fc_tp))
			elog(ERROR, "cache lookup failed for collation %u", fc_collid);

		fc_collprovider = ((Form_pg_collation) GETSTRUCT(fc_tp))->collprovider;
		fc_collisdeterministic = ((Form_pg_collation) GETSTRUCT(fc_tp))->collisdeterministic;
		fc_collencoding = ((Form_pg_collation) GETSTRUCT(fc_tp))->collencoding;

		fc_datum = SysCacheGetAttr(COLLOID, fc_tp, Anum_pg_collation_collcollate, &fc_isnull);
		if (!fc_isnull)
			fc_collcollate = TextDatumGetCString(fc_datum);
		else
			fc_collcollate = NULL;

		fc_datum = SysCacheGetAttr(COLLOID, fc_tp, Anum_pg_collation_collctype, &fc_isnull);
		if (!fc_isnull)
			fc_collctype = TextDatumGetCString(fc_datum);
		else
			fc_collctype = NULL;

		fc_datum = SysCacheGetAttr(COLLOID, fc_tp, Anum_pg_collation_colliculocale, &fc_isnull);
		if (!fc_isnull)
			fc_colliculocale = TextDatumGetCString(fc_datum);
		else
			fc_colliculocale = NULL;

		ReleaseSysCache(fc_tp);

		/*
		 * 复制“默认”排序规则是不允许的，因为大多数代码
		 * 检查DEFAULT_COLLATION_OID而不是COLLPROVIDER_DEFAULT，
		 * 因此，拥有第二个COLLPROVIDER_DEFAULT的排序规则将
		 * 无法工作，并可能导致某些代码混乱或崩溃。 这可以通过
		 * 一些工作修复。
		 */
		if (fc_collprovider == COLLPROVIDER_DEFAULT)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("collation \"default\" cannot be copied")));
	}
	else
	{
		char	   *fc_collproviderstr = NULL;

		fc_collcollate = NULL;
		fc_collctype = NULL;
		fc_colliculocale = NULL;

		if (fc_providerEl)
			fc_collproviderstr = defGetString(fc_providerEl);

		if (fc_deterministicEl)
			fc_collisdeterministic = defGetBoolean(fc_deterministicEl);
		else
			fc_collisdeterministic = true;

		if (fc_versionEl)
			fc_collversion = defGetString(fc_versionEl);

		if (fc_collproviderstr)
		{
			if (pg_strcasecmp(fc_collproviderstr, "icu") == 0)
				fc_collprovider = COLLPROVIDER_ICU;
			else if (pg_strcasecmp(fc_collproviderstr, "libc") == 0)
				fc_collprovider = COLLPROVIDER_LIBC;
			else
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("unrecognized collation provider: %s",
								fc_collproviderstr)));
		}
		else
			fc_collprovider = COLLPROVIDER_LIBC;

		if (fc_localeEl)
		{
			if (fc_collprovider == COLLPROVIDER_LIBC)
			{
				fc_collcollate = defGetString(fc_localeEl);
				fc_collctype = defGetString(fc_localeEl);
			}
			else
				fc_colliculocale = defGetString(fc_localeEl);
		}

		if (fc_lccollateEl)
			fc_collcollate = defGetString(fc_lccollateEl);

		if (fc_lcctypeEl)
			fc_collctype = defGetString(fc_lcctypeEl);

		if (fc_collprovider == COLLPROVIDER_LIBC)
		{
			if (!fc_collcollate)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("parameter \"lc_collate\" must be specified")));

			if (!fc_collctype)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("parameter \"lc_ctype\" must be specified")));
		}
		else if (fc_collprovider == COLLPROVIDER_ICU)
		{
			if (!fc_colliculocale)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("parameter \"locale\" must be specified")));
		}

		/*
		 * 当前仅支持ICU的非确定性排序规则，
		 * 因为这是唯一可以产生区别的情况。因此，我们可以
		 * 节省为其他提供者编写代码。
		 */
		if (!fc_collisdeterministic && fc_collprovider != COLLPROVIDER_ICU)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("nondeterministic collations not supported with this provider")));

		if (fc_collprovider == COLLPROVIDER_ICU)
		{
#ifdef USE_ICU
			/*
			 * 我们可以创建collencoding == 数据库
			 * 编码的ICU排序规则，但似乎最好使用-1，以使其匹配
			 * initdb将创建ICU排序规则的方式。然而，仅在
			 * 当前数据库的编码得到支持时才允许创建一个。 
			 * 否则，排序规则就没有用，而且我们会遇到诸如无法
			 * 删除排序规则之类的意外行为。
			 *
			 * 在!USE_ICU时跳过此测试，因为我们希望
			 * 抛出的错误不会直到稍后才抛出。
			 */
			if (!is_encoding_supported_by_icu(GetDatabaseEncoding()))
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("current database's encoding is not supported with this provider")));
#endif
			fc_collencoding = -1;
		}
		else
		{
			fc_collencoding = GetDatabaseEncoding();
			check_encoding_locale_matches(fc_collencoding, fc_collcollate, fc_collctype);
		}
	}

	if (!fc_collversion)
		fc_collversion = get_collation_actual_version(fc_collprovider, fc_collprovider == COLLPROVIDER_ICU ? fc_colliculocale : fc_collcollate);

	fc_newoid = CollationCreate(fc_collName,
							 fc_collNamespace,
							 GetUserId(),
							 fc_collprovider,
							 fc_collisdeterministic,
							 fc_collencoding,
							 fc_collcollate,
							 fc_collctype,
							 fc_colliculocale,
							 fc_collversion,
							 fc_if_not_exists,
							 false);	/* 不安静 */

	if (!OidIsValid(fc_newoid))
		return InvalidObjectAddress;

	/*
	 * 检查区域设置是否可以加载。 注意：pg_newlocale_from_collation
	 * 仅应在非C等效区域设置上调用。
	 */
	CommandCounterIncrement();
	if (!lc_collate_is_c(fc_newoid) || !lc_ctype_is_c(fc_newoid))
		(void) pg_newlocale_from_collation(fc_newoid);

	ObjectAddressSet(fc_address, CollationRelationId, fc_newoid);

	return fc_address;
}

/*
 * ALTER COLLATION SET SCHEMA和RENAME的子程序
 *
 * 给定命名空间中是否已经存在同名的排序规则？
 * 如果是，抛出适当的错误信息。
 */
void IsThereCollationInNamespace(const char *fc_collname, Oid fc_nspOid)
{
	/* 确保名称在新模式中不存在 */
	if (SearchSysCacheExists3(COLLNAMEENCNSP,
							  CStringGetDatum(fc_collname),
							  Int32GetDatum(GetDatabaseEncoding()),
							  ObjectIdGetDatum(fc_nspOid)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("collation \"%s\" for encoding \"%s\" already exists in schema \"%s\"",
						fc_collname, GetDatabaseEncodingName(),
						get_namespace_name(fc_nspOid))));

	/* 也不能与任何编码条目匹配 */
	if (SearchSysCacheExists3(COLLNAMEENCNSP,
							  CStringGetDatum(fc_collname),
							  Int32GetDatum(-1),
							  ObjectIdGetDatum(fc_nspOid)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("collation \"%s\" already exists in schema \"%s\"",
						fc_collname, get_namespace_name(fc_nspOid))));
}

/*
 * ALTER COLLATION
 */
ObjectAddress AlterCollation(AlterCollationStmt *fc_stmt)
{
	Relation	fc_rel;
	Oid			fc_collOid;
	HeapTuple	fc_tup;
	Form_pg_collation fc_collForm;
	Datum		fc_datum;
	bool		fc_isnull;
	char	   *fc_oldversion;
	char	   *fc_newversion;
	ObjectAddress fc_address;

	fc_rel = table_open(CollationRelationId, RowExclusiveLock);
	fc_collOid = get_collation_oid(fc_stmt->collname, false);

	if (!pg_collation_ownercheck(fc_collOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_COLLATION,
					   NameListToString(fc_stmt->collname));

	fc_tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(fc_collOid));
	if (!HeapTupleIsValid(fc_tup))
		elog(ERROR, "cache lookup failed for collation %u", fc_collOid);

	fc_collForm = (Form_pg_collation) GETSTRUCT(fc_tup);
	fc_datum = SysCacheGetAttr(COLLOID, fc_tup, Anum_pg_collation_collversion, &fc_isnull);
	fc_oldversion = fc_isnull ? NULL : TextDatumGetCString(fc_datum);

	fc_datum = SysCacheGetAttr(COLLOID, fc_tup, fc_collForm->collprovider == COLLPROVIDER_ICU ? Anum_pg_collation_colliculocale : Anum_pg_collation_collcollate, &fc_isnull);
	if (fc_isnull)
		elog(ERROR, "unexpected null in pg_collation");
	fc_newversion = get_collation_actual_version(fc_collForm->collprovider, TextDatumGetCString(fc_datum));

	/* 不能从 NULL 改为非 NULL 或反之 */
	if ((!fc_oldversion && fc_newversion) || (fc_oldversion && !fc_newversion))
		elog(ERROR, "invalid collation version change");
	else if (fc_oldversion && fc_newversion && strcmp(fc_newversion, fc_oldversion) != 0)
	{
		bool		fc_nulls[Natts_pg_collation];
		bool		fc_replaces[Natts_pg_collation];
		Datum		fc_values[Natts_pg_collation];

		ereport(NOTICE,
				(errmsg("changing version from %s to %s",
						fc_oldversion, fc_newversion)));

		memset(fc_values, 0, sizeof(fc_values));
		memset(fc_nulls, false, sizeof(fc_nulls));
		memset(fc_replaces, false, sizeof(fc_replaces));

		fc_values[Anum_pg_collation_collversion - 1] = CStringGetTextDatum(fc_newversion);
		fc_replaces[Anum_pg_collation_collversion - 1] = true;

		fc_tup = heap_modify_tuple(fc_tup, RelationGetDescr(fc_rel),
								fc_values, fc_nulls, fc_replaces);
	}
	else
		ereport(NOTICE,
				(errmsg("version has not changed")));

	CatalogTupleUpdate(fc_rel, &fc_tup->t_self, fc_tup);

	InvokeObjectPostAlterHook(CollationRelationId, fc_collOid, 0);

	ObjectAddressSet(fc_address, CollationRelationId, fc_collOid);

	heap_freetuple(fc_tup);
	table_close(fc_rel, NoLock);

	return fc_address;
}


Datum pg_collation_actual_version(PG_FUNCTION_ARGS)
{
	Oid			fc_collid = PG_GETARG_OID(0);
	HeapTuple	fc_tp;
	char		fc_collprovider;
	Datum		fc_datum;
	bool		fc_isnull;
	char	   *fc_version;

	fc_tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(fc_collid));
	if (!HeapTupleIsValid(fc_tp))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("collation with OID %u does not exist", fc_collid)));

	fc_collprovider = ((Form_pg_collation) GETSTRUCT(fc_tp))->collprovider;

	if (fc_collprovider != COLLPROVIDER_DEFAULT)
	{
		fc_datum = SysCacheGetAttr(COLLOID, fc_tp, fc_collprovider == COLLPROVIDER_ICU ? Anum_pg_collation_colliculocale : Anum_pg_collation_collcollate, &fc_isnull);
		if (fc_isnull)
			elog(ERROR, "unexpected null in pg_collation");
		fc_version = get_collation_actual_version(fc_collprovider, TextDatumGetCString(fc_datum));
	}
	else
		fc_version = NULL;

	ReleaseSysCache(fc_tp);

	if (fc_version)
		PG_RETURN_TEXT_P(cstring_to_text(fc_version));
	else
		PG_RETURN_NULL();
}


/* 我们会在pg_import_system_collations中使用“locale -a”吗？ */
#if defined(HAVE_LOCALE_T) && !defined(WIN32)
#define READ_LOCALE_A_OUTPUT
#endif

#ifdef READ_LOCALE_A_OUTPUT
/*
 * “标准化”libc区域设置名称，去除编码标签，例如
 * “.utf8”（例如，“en_US.utf8” -> “en_US”，但“br_FR.iso885915@euro”
 * -> “br_FR@euro”）。如果生成了一个新的、不同的名称，
 * 则返回true。
 */
static bool fc_normalize_libc_locale_name(char *fc_new, const char *fc_old)
{
	char	   *fc_n = fc_new;
	const char *fc_o = fc_old;
	bool		fc_changed = false;

	while (*fc_o)
	{
		if (*fc_o == '.')
		{
			/* 跳过编码标签，例如“.utf8”或“.UTF-8” */
			fc_o++;
			while ((*fc_o >= 'A' && *fc_o <= 'Z')
				   || (*fc_o >= 'a' && *fc_o <= 'z')
				   || (*fc_o >= '0' && *fc_o <= '9')
				   || (*fc_o == '-'))
				fc_o++;
			fc_changed = true;
		}
		else
			*fc_n++ = *fc_o++;
	}
	*fc_n = '\0';

	return fc_changed;
}

/*
 * CollAliasData项目的qsort比较器
 */
static int fc_cmpaliases(const void *fc_a, const void *fc_b)
{
	const CollAliasData *fc_ca = (const CollAliasData *) fc_a;
	const CollAliasData *fc_cb = (const CollAliasData *) fc_b;

	/* 比较localename就足够了，因为其他字段是派生的 */
	return strcmp(fc_ca->localename, fc_cb->localename);
}
#endif							/* READ_LOCALE_A_OUTPUT */


#ifdef USE_ICU
/*
 * 获取区域设置名称的ICU语言标签。
 * 结果是一个palloc'd字符串。
 */
static char * get_icu_language_tag(const char *fc_localename)
{
	char		fc_buf[ULOC_FULLNAME_CAPACITY];
	UErrorCode	fc_status;

	fc_status = U_ZERO_ERROR;
	uloc_toLanguageTag(fc_localename, fc_buf, sizeof(fc_buf), true, &fc_status);
	if (U_FAILURE(fc_status))
		ereport(ERROR,
				(errmsg("could not convert locale name \"%s\" to language tag: %s",
						fc_localename, u_errorName(fc_status))));

	return pstrdup(fc_buf);
}

/*
 * 获取ICU区域设置的注释（具体来说，显示名称）。
 * 结果是一个palloc'd字符串，如果我们无法获取注释
 * 或发现其不是全部ASCII，则返回NULL。 
 * （我们不能接受非ASCII
 * 注释，因为template0的内容必须与编码无关。）
 */
static char * get_icu_locale_comment(const char *fc_localename)
{
	UErrorCode	fc_status;
	UChar		fc_displayname[128];
	int32		fc_len_uchar;
	int32		fc_i;
	char	   *fc_result;

	fc_status = U_ZERO_ERROR;
	fc_len_uchar = uloc_getDisplayName(fc_localename, "en",
									fc_displayname, lengthof(fc_displayname),
									&fc_status);
	if (U_FAILURE(fc_status))
		return NULL;			/* 没有充分理由引发错误 */

	/* 检查非ASCII注释（无法为此使用pg_is_ascii） */
	for (fc_i = 0; fc_i < fc_len_uchar; fc_i++)
	{
		if (fc_displayname[fc_i] > 127)
			return NULL;
	}

	/* 好的，转录 */
	fc_result = palloc(fc_len_uchar + 1);
	for (fc_i = 0; fc_i < fc_len_uchar; fc_i++)
		fc_result[fc_i] = fc_displayname[fc_i];
	fc_result[fc_len_uchar] = '\0';

	return fc_result;
}
#endif							/* USE_ICU */


/*
 * pg_import_system_collations：将已知系统排序规则添加到pg_collation
 */
Datum pg_import_system_collations(PG_FUNCTION_ARGS)
{
	Oid			fc_nspid = PG_GETARG_OID(0);
	int			fc_ncreated = 0;

	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser to import system collations")));

	if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(fc_nspid)))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_SCHEMA),
				 errmsg("schema with OID %u does not exist", fc_nspid)));

	/* 加载已知于libc的排序规则，使用"locale -a"列举它们 */
#ifdef READ_LOCALE_A_OUTPUT
	{
		FILE	   *fc_locale_a_handle;
		char		fc_localebuf[LOCALE_NAME_BUFLEN];
		int			fc_nvalid = 0;
		Oid			fc_collid;
		CollAliasData *fc_aliases;
		int			fc_naliases,
					fc_maxaliases,
					fc_i;

		/* 可扩展的别名数组 */
		fc_maxaliases = 100;
		fc_aliases = (CollAliasData *) palloc(fc_maxaliases * sizeof(CollAliasData));
		fc_naliases = 0;

		fc_locale_a_handle = OpenPipeStream("locale -a", "r");
		if (fc_locale_a_handle == NULL)
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not execute command \"%s\": %m",
							"locale -a")));

		while (fgets(fc_localebuf, sizeof(fc_localebuf), fc_locale_a_handle))
		{
			size_t		fc_len;
			int			fc_enc;
			char		fc_alias[LOCALE_NAME_BUFLEN];

			fc_len = strlen(fc_localebuf);

			if (fc_len == 0 || fc_localebuf[fc_len - 1] != '\n')
			{
				elog(DEBUG1, "skipping locale with too-long name: \"%s\"", fc_localebuf);
				continue;
			}
			fc_localebuf[fc_len - 1] = '\0';

			/*
			 * 一些系统的区域设置名称并不完全由
			 * ASCII 字母组成（例如 "bokm&aring;l" 或 "fran&ccedil;ais"）。
			 * 这非常愚蠢，因为我们需要区域设置本身来
			 * 解释非 ASCII 字符。我们对此无能为力，
			 * 所以我们将它们过滤掉。
			 */
			if (!pg_is_ascii(fc_localebuf))
			{
				elog(DEBUG1, "skipping locale with non-ASCII name: \"%s\"", fc_localebuf);
				continue;
			}

			fc_enc = pg_get_encoding_from_locale(fc_localebuf, false);
			if (fc_enc < 0)
			{
				elog(DEBUG1, "skipping locale with unrecognized encoding: \"%s\"",
					 fc_localebuf);
				continue;
			}
			if (!PG_VALID_BE_ENCODING(fc_enc))
			{
				elog(DEBUG1, "skipping locale with client-only encoding: \"%s\"", fc_localebuf);
				continue;
			}
			if (fc_enc == PG_SQL_ASCII)
				continue;		/* C/POSIX 已经在目录中 */

			/* 计算操作系统中找到的有效区域设置 */
			fc_nvalid++;

			/*
			 * 创建一个与区域设置同名的排序规则，但如果已经存在
			 * 则静默不做任何操作。这是我们即使在initdb时
			 * 也需要的行为，因为某些版本的 "locale -a"
			 * 可能会多次报告相同的区域设置名称。并且这在
			 * 后续的导入运行中也很方便，因为你几乎总是希望
			 * 添加新的区域设置，而不是大量讨论现有的区域设置。
			 */
			fc_collid = CollationCreate(fc_localebuf, fc_nspid, GetUserId(),
									 COLLPROVIDER_LIBC, true, fc_enc,
									 fc_localebuf, fc_localebuf, NULL,
									 get_collation_actual_version(COLLPROVIDER_LIBC, fc_localebuf),
									 true, true);
			if (OidIsValid(fc_collid))
			{
				fc_ncreated++;

				/* 必须在插入之间执行CCI，以正确处理重复项 */
				CommandCounterIncrement();
			}

			/*
			 * 除了 "en_US.utf8" 之外，还生成诸如 "en_US" 的别名
			 * 以方便使用。请注意，排序规则名称在编码上是唯一的，
			 * 所以这不会与 LATIN1 的 "en_US" 冲突，
			 * 比如说。
			 *
			 * 然而，它可能与我们稍后在 "locale -a" 输出中看到的名称
			 * 冲突。因此请保存别名，并在读取完所有输出后
			 * 尝试添加它们。
			 */
			if (fc_normalize_libc_locale_name(fc_alias, fc_localebuf))
			{
				if (fc_naliases >= fc_maxaliases)
				{
					fc_maxaliases *= 2;
					fc_aliases = (CollAliasData *)
						repalloc(fc_aliases, fc_maxaliases * sizeof(CollAliasData));
				}
				fc_aliases[fc_naliases].localename = pstrdup(fc_localebuf);
				fc_aliases[fc_naliases].alias = pstrdup(fc_alias);
				fc_aliases[fc_naliases].enc = fc_enc;
				fc_naliases++;
			}
		}

		ClosePipeStream(fc_locale_a_handle);

		/*
		 * 在处理别名之前，按区域设置名称对它们排序。其要点
		 * 是，如果 "locale -a" 给我们多个具有相同编码和基本名称的
		 * 区域设置名称，比如 "en_US.utf8" 和 "en_US.utf-8"，我们
		 * 希望从中选择一个确定性的。按 ASCII 排序的第一个
		 * 是一个足够好的规则。（在 PG 10 之前，initdb.c 中与
		 * 该逻辑对应的代码有一个额外的排序规则，优先考虑名称
		 * 与别名完全匹配的区域设置，如果有的话。我们
		 * 在这里不需要考虑这一点，因为我们已经在上面创建了
		 * 这样的 pg_collation 条目，而那个条目将胜出。）
		 */
		if (fc_naliases > 1)
			qsort((void *) fc_aliases, fc_naliases, sizeof(CollAliasData), fc_cmpaliases);

		/* 现在添加别名，忽略与现有条目匹配的任何别名 */
		for (fc_i = 0; fc_i < fc_naliases; fc_i++)
		{
			char	   *fc_locale = fc_aliases[fc_i].localename;
			char	   *fc_alias = fc_aliases[fc_i].alias;
			int			fc_enc = fc_aliases[fc_i].enc;

			fc_collid = CollationCreate(fc_alias, fc_nspid, GetUserId(),
									 COLLPROVIDER_LIBC, true, fc_enc,
									 fc_locale, fc_locale, NULL,
									 get_collation_actual_version(COLLPROVIDER_LIBC, fc_locale),
									 true, true);
			if (OidIsValid(fc_collid))
			{
				fc_ncreated++;

				CommandCounterIncrement();
			}
		}

		/* 如果 "locale -a" 似乎出现故障，则给予警告 */
		if (fc_nvalid == 0)
			ereport(WARNING,
					(errmsg("no usable system locales were found")));
	}
#endif							/* READ_LOCALE_A_OUTPUT */

	/*
	 * 加载已知于ICU的排序规则
	 *
	 * 我们使用 uloc_countAvailable()/uloc_getAvailable() 而不是
	 * ucol_countAvailable()/ucol_getAvailable()。前者返回一组完整的
	 * 语言+区域组合，而后者仅返回与语言的
	 * 基本排序规则不同的语言+区域组合。因此可能没有
	 * de-DE 或 en-GB，这会造成困惑。
	 */
#ifdef USE_ICU
	{
		int			fc_i;

		/*
		 * 将循环起始设置为-1，以悄悄地插入根区域设置，而不引入
		 * 过多的代码重复。
		 */
		for (fc_i = -1; fc_i < uloc_countAvailable(); fc_i++)
		{
			const char *fc_name;
			char	   *fc_langtag;
			char	   *fc_icucomment;
			const char *fc_iculocstr;
			Oid			fc_collid;

			if (fc_i == -1)
				fc_name = "";		/* ICU 根区域设置 */
			else
				fc_name = uloc_getAvailable(fc_i);

			fc_langtag = get_icu_language_tag(fc_name);
			fc_iculocstr = U_ICU_VERSION_MAJOR_NUM >= 54 ? fc_langtag : fc_name;

			/*
			 * 要谨慎以免允许任何非 ASCII 字符串进入
			 * pg_collation
			 */
			if (!pg_is_ascii(fc_langtag) || !pg_is_ascii(fc_iculocstr))
				continue;

			fc_collid = CollationCreate(psprintf("%s-x-icu", fc_langtag),
									 fc_nspid, GetUserId(),
									 COLLPROVIDER_ICU, true, -1,
									 NULL, NULL, fc_iculocstr,
									 get_collation_actual_version(COLLPROVIDER_ICU, fc_iculocstr),
									 true, true);
			if (OidIsValid(fc_collid))
			{
				fc_ncreated++;

				CommandCounterIncrement();

				fc_icucomment = get_icu_locale_comment(fc_name);
				if (fc_icucomment)
					CreateComments(fc_collid, CollationRelationId, 0,
								   fc_icucomment);
			}
		}
	}
#endif							/* USE_ICU */

	PG_RETURN_INT32(fc_ncreated);
}
